﻿
// <copyright file="BindingListAdapter.cs" company="Subhadra Software Solutions">
//   
// </copyright>
// <summary>
//   The binding list adapter.
// </summary>


namespace SubhadraSolutions.Sharp.Utils.ComponentModel
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;

    using SubhadraSolutions.Sharp.Utils.Collections.Generic;
    using SubhadraSolutions.Sharp.Utils.Threading;
    using SubhadraSolutions.Sharp.Utils.Validation;

    /// <summary>
    /// The DispatcherAwareBindingList class.
    /// </summary>
    /// <typeparam name="T">
    /// </typeparam>
    public class DispatcherAwareBindingList<T> : AbstractListDecorator<T>, IBindingList,  ICancelAddNew, IRaiseItemChangedEvents
    {
        /// <summary>
        /// The dispatcher provider
        /// </summary>
        private IDispatcherProvider dispatcherProvider;

        /// <summary>
        /// The binding list
        /// </summary>
        private IBindingList bindingList;

        /// <summary>
        /// The list changed event handler
        /// </summary>
        private ListChangedEventHandler listChangedEventHandler;

        /// <summary>
        /// The adding new event handler
        /// </summary>
        private AddingNewEventHandler addingNewEventHandler;

        /// <summary>
        /// Initializes a new instance of the <see cref="DispatcherAwareBindingList{T}" /> class.
        /// </summary>
        /// <param name="dispatcherProvider">The dispatcher provider.</param>
        public DispatcherAwareBindingList(IDispatcherProvider dispatcherProvider)
            : base(new BindingList<T>())
        {
            this.Initialize(dispatcherProvider, null);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DispatcherAwareBindingList{T}"/> class.
        /// </summary>
        /// <param name="dispatcherProvider">The dispatcher provider.</param>
        /// <param name="adaptedList">The adapted list.</param>
        public DispatcherAwareBindingList(IDispatcherProvider dispatcherProvider, IList<T> adaptedList)
            : base(new BindingList<T>(adaptedList))
        {
            Guard.ArgumentNotNull(adaptedList, "adaptedList");
            this.Initialize(dispatcherProvider, adaptedList);
        }

        /// <summary>
        /// Initializes the specified dispatcher provider.
        /// </summary>
        /// <param name="dispatcherProvider">The dispatcher provider.</param>
        /// <param name="adaptedList">The adapted list.</param>
        private void Initialize(IDispatcherProvider dispatcherProvider, IList<T> adaptedList)
        {
            Guard.ArgumentNotNull(dispatcherProvider, "dispatcherProvider");
            this.dispatcherProvider = dispatcherProvider;

            var bindingListObject = (BindingList<T>)this.AdaptedList;
            bindingListObject.AddingNew += bindingListObject_AddingNew;
            bindingListObject.ListChanged += bindingListObject_ListChanged;
            this.bindingList = bindingListObject;
        }

        /// <summary>
        /// Handles the ListChanged event of the bindingListObject control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="ListChangedEventArgs"/> instance containing the event data.</param>
        private void bindingListObject_ListChanged(object sender, ListChangedEventArgs e)
        {
            GeneralHelper.SafeInvoke(this.listChangedEventHandler, this, e);
        }

        /// <summary>
        /// Handles the AddingNew event of the bindingListObject control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="AddingNewEventArgs"/> instance containing the event data.</param>
        private void bindingListObject_AddingNew(object sender, AddingNewEventArgs e)
        {
            GeneralHelper.SafeInvoke(this.addingNewEventHandler, this, e);
        }

        /// <summary>
        /// Occurs when [list changed].
        /// </summary>
        public event ListChangedEventHandler ListChanged
        {
            add
            {
                IDispatcher dispatcher = null;
                if (this.dispatcherProvider != null)
                {
                    dispatcher = this.dispatcherProvider.GetDispatcher(value.Target);
                }

                if (dispatcher == null)
                {
                    this.listChangedEventHandler += value;
                }
                else
                {
                    this.listChangedEventHandler += (sender, e) => dispatcher.Dispatch(() => value(sender, e));
                }
            }

            remove
            {
                this.listChangedEventHandler -= value;
            }
        }

        /// <summary>
        /// Occurs when [adding new].
        /// </summary>
        public event AddingNewEventHandler AddingNew
        {
            add
            {
                IDispatcher dispatcher = null;
                if (this.dispatcherProvider != null)
                {
                    dispatcher = this.dispatcherProvider.GetDispatcher(value.Target);
                }

                if (dispatcher == null)
                {
                    this.addingNewEventHandler += value;
                }
                else
                {
                    this.addingNewEventHandler += (sender, e) => dispatcher.Dispatch(() => value(sender, e));
                }
            }

            remove
            {
                this.addingNewEventHandler -= value;
            }
        }

        /// <summary>
        /// Adds the <see cref="T:System.ComponentModel.PropertyDescriptor" /> to the indexes used for searching.
        /// </summary>
        /// <param name="property">The <see cref="T:System.ComponentModel.PropertyDescriptor" /> to add to the indexes used for searching.</param>
        public void AddIndex(PropertyDescriptor property)
        {
            this.bindingList.AddIndex(property);
        }

        /// <summary>
        /// Adds a new item to the list.
        /// </summary>
        /// <returns>
        /// The item added to the list.
        /// </returns>
        public object AddNew()
        {
            return this.bindingList.AddNew();
        }

        /// <summary>
        /// Gets whether you can update items in the list.
        /// </summary>
        public bool AllowEdit
        {
            get
            {
                return this.bindingList.AllowEdit;
            }
        }

        /// <summary>
        /// Gets whether you can add items to the list using <see cref="M:System.ComponentModel.IBindingList.AddNew" />.
        /// </summary>
        public bool AllowNew
        {
            get
            {
                return this.bindingList.AllowNew;
            }
        }

        /// <summary>
        /// Gets whether you can remove items from the list, using <see cref="M:System.Collections.IList.Remove(System.Object)" /> or <see cref="M:System.Collections.IList.RemoveAt(System.Int32)" />.
        /// </summary>
        public bool AllowRemove
        {
            get
            {
                return this.bindingList.AllowRemove;
            }
        }

        /// <summary>
        /// Sorts the list based on a <see cref="T:System.ComponentModel.PropertyDescriptor" /> and a <see cref="T:System.ComponentModel.ListSortDirection" />.
        /// </summary>
        /// <param name="property">The <see cref="T:System.ComponentModel.PropertyDescriptor" /> to sort by.</param>
        /// <param name="direction">One of the <see cref="T:System.ComponentModel.ListSortDirection" /> values.</param>
        public void ApplySort(PropertyDescriptor property, ListSortDirection direction)
        {
            this.bindingList.ApplySort(property, direction);
        }

        /// <summary>
        /// Returns the index of the row that has the given <see cref="T:System.ComponentModel.PropertyDescriptor" />.
        /// </summary>
        /// <param name="property">The <see cref="T:System.ComponentModel.PropertyDescriptor" /> to search on.</param>
        /// <param name="key">The value of the <paramref name="property" /> parameter to search for.</param>
        /// <returns>
        /// The index of the row that has the given <see cref="T:System.ComponentModel.PropertyDescriptor" />.
        /// </returns>
        public int Find(PropertyDescriptor property, object key)
        {
            return this.bindingList.Find(property, key);
        }

        /// <summary>
        /// Gets whether the items in the list are sorted.
        /// </summary>
        public bool IsSorted
        {
            get
            {
                return this.bindingList.IsSorted;
            }
        }

        /// <summary>
        /// Removes the <see cref="T:System.ComponentModel.PropertyDescriptor" /> from the indexes used for searching.
        /// </summary>
        /// <param name="property">The <see cref="T:System.ComponentModel.PropertyDescriptor" /> to remove from the indexes used for searching.</param>
        public void RemoveIndex(PropertyDescriptor property)
        {
            this.bindingList.RemoveIndex(property);
        }

        /// <summary>
        /// Removes any sort applied using <see cref="M:System.ComponentModel.IBindingList.ApplySort(System.ComponentModel.PropertyDescriptor,System.ComponentModel.ListSortDirection)" />.
        /// </summary>
        public void RemoveSort()
        {
            this.bindingList.RemoveSort();
        }

        /// <summary>
        /// Gets the direction of the sort.
        /// </summary>
        public ListSortDirection SortDirection
        {
            get
            {
                return this.bindingList.SortDirection;
            }
        }

        /// <summary>
        /// Gets the <see cref="T:System.ComponentModel.PropertyDescriptor" /> that is being used for sorting.
        /// </summary>
        public PropertyDescriptor SortProperty
        {
            get
            {
                return this.bindingList.SortProperty;
            }
        }

        /// <summary>
        /// Gets whether a <see cref="E:System.ComponentModel.IBindingList.ListChanged" /> event is raised when the list changes or an item in the list changes.
        /// </summary>
        public bool SupportsChangeNotification
        {
            get
            {
                return this.bindingList.SupportsChangeNotification;
            }
        }

        /// <summary>
        /// Gets whether the list supports searching using the <see cref="M:System.ComponentModel.IBindingList.Find(System.ComponentModel.PropertyDescriptor,System.Object)" /> method.
        /// </summary>
        public bool SupportsSearching
        {
            get
            {
                return this.bindingList.SupportsSearching;
            }
        }

        /// <summary>
        /// Gets whether the list supports sorting.
        /// </summary>
        public bool SupportsSorting
        {
            get
            {
                return this.bindingList.SupportsSorting;
            }
        }

        /// <summary>
        /// Adds the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public int Add(object value)
        {
            return this.bindingList.Add(value);
        }

        /// <summary>
        /// Determines whether [contains] [the specified value].
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public bool Contains(object value)
        {
            return this.bindingList.Contains(value);
        }

        /// <summary>
        /// Indexes the of.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public int IndexOf(object value)
        {
            return this.bindingList.IndexOf(value);
        }

        /// <summary>
        /// Inserts the specified index.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="value">The value.</param>
        public void Insert(int index, object value)
        {
            this.bindingList.Insert(index, value);
        }

        /// <summary>
        /// Gets a value indicating whether this instance is fixed size.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is fixed size; otherwise, <c>false</c>.
        /// </value>
        public bool IsFixedSize
        {
            get
            {
                return this.bindingList.IsFixedSize;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is read only.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is read only; otherwise, <c>false</c>.
        /// </value>
        public override bool IsReadOnly
        {
            get
            {
                return this.bindingList.IsReadOnly;
            }
        }

        /// <summary>
        /// Removes the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        public void Remove(object value)
        {
            this.bindingList.Remove(value);
        }

        /// <summary>
        /// Copies to.
        /// </summary>
        /// <param name="array">The array.</param>
        /// <param name="index">The index.</param>
        public void CopyTo(Array array, int index)
        {
            this.bindingList.CopyTo(array, index);
        }

        /// <summary>
        /// Gets a value indicating whether this instance is synchronized.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is synchronized; otherwise, <c>false</c>.
        /// </value>
        public bool IsSynchronized
        {
            get
            {
                return this.bindingList.IsSynchronized;
            }
        }

        /// <summary>
        /// Gets the synchronize root.
        /// </summary>
        /// <value>
        /// The synchronize root.
        /// </value>
        public object SyncRoot
        {
            get
            {
                return this.bindingList.SyncRoot;
            }
        }

        /// <summary>
        /// Discards a pending new item from the collection.
        /// </summary>
        /// <param name="itemIndex">The index of the item that was previously added to the collection.</param>
        public void CancelNew(int itemIndex)
        {
            ((ICancelAddNew)this.bindingList).CancelNew(itemIndex);
        }

        /// <summary>
        /// Commits a pending new item to the collection.
        /// </summary>
        /// <param name="itemIndex">The index of the item that was previously added to the collection.</param>
        public void EndNew(int itemIndex)
        {
            ((ICancelAddNew)this.bindingList).EndNew(itemIndex);
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="T:System.ComponentModel.IRaiseItemChangedEvents" /> object raises <see cref="E:System.ComponentModel.IBindingList.ListChanged" /> events.
        /// </summary>
        public bool RaisesItemChangedEvents
        {
            get
            {
                return ((IRaiseItemChangedEvents)this.bindingList).RaisesItemChangedEvents;
            }
        }
    }
}